package io.task.cc.network.maintain;

import io.task.cc.network.ChannelContext;
import io.task.cc.network.utils.lock.LockUtils;
import io.task.cc.network.utils.lock.LockedMap;
import io.task.cc.network.utils.lock.LockedSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 一对多  (userid <--> ChannelContext)<br>
 */
public class Users {
    private static final Logger log = LoggerFactory.getLogger(Users.class);
    /**
     * key: userid
     * value: ChannelContext
     */
    private final LockedMap<String, LockedSet<ChannelContext>> map = new LockedMap<>(new HashMap<String, LockedSet<ChannelContext>>());

    /**
     * 绑定userid.
     *
     * @param userid         the userid
     * @param channelContext the channel context
     * @author tanyaowu
     */
    public void bind(String userid, ChannelContext channelContext) {


        if (userid == null) {
            return;
        }

        try {
            LockedSet<ChannelContext> lockedSet = map.get(userid);
            if (lockedSet == null) {
                LockUtils.runWriteOrWaitRead("_tio_users_bind__" + userid, this, () -> {
//					@Override
//					public void read() {
//					}

//					@Override
//					public void write() {
//						SetWithLock<ChannelContext> setWithLock = mapWithLock.get(userid);
                    if (map.get(userid) == null) {
//							setWithLock = new SetWithLock<>(new HashSet<ChannelContext>());
                        map.put(userid, new LockedSet<>(new HashSet<ChannelContext>()));
                    }
//					}
                });
                lockedSet = map.get(userid);
            }
            lockedSet.add(channelContext);

            channelContext.setUserid(userid);
        } catch (Throwable e) {
            log.error("", e);
        }

    }

    /**
     * Find.
     *
     * @param userid the userid
     * @return the channel context
     */
    public LockedSet<ChannelContext> find(String userid) {


        if (userid == null) {
            return null;
        }

        return map.get(userid);
    }

    /**
     * 解除channelContext绑定的userid
     *
     * @param channelContext the channel context
     */
    public void unbind(ChannelContext channelContext) {


        String userid = channelContext.userid;
        if (userid == null) {
            log.debug("{}, {}, 并没有绑定用户", channelContext.config.getName(), channelContext);
            return;
        }

        try {
            LockedSet<ChannelContext> set = map.get(userid);
            if (set == null) {
                log.warn("{}, {}, userid:{}, 没有找到对应的SetWithLock", channelContext.config.getName(), channelContext, userid);
                return;
            }

            set.remove(channelContext);

            if (set.size() == 0) {
                map.remove(userid);
            }

            channelContext.setUserid(null);
        } catch (Throwable e) {
            log.error(e.toString(), e);
        }
    }

    /**
     * 解除tioConfig范围内所有ChannelContext的 userid绑定
     *
     * @param userid the userid
     * @author tanyaowu
     */
    public void unbind(String userid) {

        if (userid == null) {
            return;
        }

        try {
            Lock lock = map.writeLock();
            lock.lock();
            try {
                Map<String, LockedSet<ChannelContext>> map = this.map.getT();
                LockedSet<ChannelContext> lockedSet = map.get(userid);
                if (lockedSet == null) {
                    return;
                }

                WriteLock writeLock = lockedSet.writeLock();
                writeLock.lock();
                try {
                    Set<ChannelContext> set = lockedSet.getT();
                    if (set.size() > 0) {
                        for (ChannelContext channelContext : set) {
                            channelContext.setUserid(null);
                        }
                        set.clear();
                    }

                    map.remove(userid);
                } catch (Throwable e) {
                    log.error(e.getMessage(), e);
                } finally {
                    writeLock.unlock();
                }

            } catch (Throwable e) {
                throw e;
            } finally {
                lock.unlock();
            }
        } catch (Throwable e) {
            log.error(e.toString(), e);
        }
    }
}
